// TODO: config macro
// if add #define SUITE_IMPL
// then include Suite.h
// this file will be header only file
// it also deps on Test.h
#ifndef SUITE_H
#define SUITE_H
#include <vector>
#include <stdexcept>
#define TEST_IMPL
#include "Test.h"
using std::logic_error;
using std::vector;

namespace TestSuite
{
    class TestSuiteError : public logic_error
    {
    public:
        TestSuiteError(const string &s = "")
            : logic_error(s) {}
    };

    class Suite
    {
        string name;
        ostream *osptr;
        vector<Test *> tests;
        void reset();
        // Disallow ctor
        Suite(const Suite &);
        Suite &operator=(const Suite &);

    public:
        Suite(const string &name, ostream *optr = &cout)
            : name(name) { osptr = optr; }
        string getName() const
        {
            return name;
        }
        long getNumPassed() const;
        long getNumFailed() const;
        const ostream *getStream() const { return osptr; }
        void addTest(Test *t) throw(TestSuiteError);
        void addSuite(const Suite &);
        void run(); // Call Test::run() repeatedly
        long report() const;
        void free(); // delete teste
    };
} // namespace TestSuite

#endif // SUITE_H

#ifdef SUITE_IMPL
#include <iostream>
#include <cassert>
#include <cstddef>
using namespace std;
using namespace TestSuite;

void Suite::addTest(Test *t) throw(TestSuiteError)
{
    // verify test is valid and has a stream
    if (t == 0)
        throw TestSuiteError("Null test in Suite::addTest");
    else if (osptr && !t->getStream())
    {
        t->setStream(osptr);
    }
    tests.push_back(t);
    t->reset();
}

void Suite::addSuite(const Suite &s)
{
    for (size_t i = 0; i < s.tests.size(); ++i)
    {
        assert(tests[i]);
        addTest(s.tests[i]);
    }
}

void Suite::free()
{
    for (size_t i = 0; i < tests.size(); ++i)
    {
        delete tests[i];
        tests[i] = 0;
    }
}

void Suite::run()
{
    reset();
    for (size_t i = 0; i < tests.size(); ++i)
    {
        assert(tests[i]);
        tests[i]->run();
    }
}

long Suite::report() const
{
    if (osptr)
    {
        long totFail = 0;
        *osptr << "Suite \"" << name
               << "\"\n========";
        size_t i;
        for (i = 0; i < name.size(); ++i)
            *osptr << '=';
        *osptr << "=" << endl;
        for (i = 0; i < tests.size(); ++i)
        {
            assert(tests[i]);
            totFail += tests[i]->report();
        }
        *osptr << "========";
        for (i = 0; i < name.size(); ++i)
            *osptr << '=';
        *osptr << "=" << endl;
        return totFail;
    }
    else
    {
        return getNumFailed();
    }
}

long Suite::getNumPassed() const
{
    long totPass = 0;
    for (size_t i = 0; i < tests.size(); ++i)
    {
        assert(tests[i]);
        totPass += tests[i]->getNumPassed();
    }
    return totPass;
}

long Suite::getNumFailed() const
{
    long totFail = 0;
    for (size_t i = 0; i < tests.size(); ++i)
    {
        assert(tests[i]);
        totFail += tests[i]->getNumFailed();
    }
    return totFail;
}

void Suite::reset()
{
    for (size_t i = 0; i < tests.size(); ++i)
    {
        assert(tests[i]);
        tests[i]->reset();
    }
}
#endif // SUITE_IMPL